package com.jspxcms.core.web.fore;

import cn.hutool.core.util.StrUtil;
import com.jspxcms.common.web.Servlets;
import com.jspxcms.core.constant.Constants;
import com.jspxcms.core.domain.GlobalRegister;
import com.jspxcms.core.domain.Site;
import com.jspxcms.core.domain.User;
import com.jspxcms.core.domain.UserDetail;
import com.jspxcms.core.security.LoginType;
import com.jspxcms.core.security.UserToken;
import com.jspxcms.core.service.UserService;
import com.jspxcms.core.service.UserShiroService;
import com.jspxcms.core.support.Context;
import com.jspxcms.core.support.ForeContext;
import com.jspxcms.core.support.Response;
import com.xkcoding.justauth.AuthRequestFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.jspxcms.core.security.CmsAuthenticationFilter.FALLBACK_URL_PARAM;
import static org.apache.shiro.web.filter.authc.FormAuthenticationFilter.DEFAULT_USERNAME_PARAM;

@Slf4j
@Controller
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class OauthController {

    private final AuthRequestFactory factory;

    @Autowired
    private UserShiroService userShiroService;
    @Autowired
    private UserService userService;

    public static final String OAUTH_REGISTER_TEMPLATE = "sys_oauth_register.html";


    /**
     * 登录类型
     */
    @ResponseBody
    @GetMapping("/oauth")
    public Map<String, String> loginType() {
        List<String> oauthList = factory.oauthList();
        return oauthList.stream().collect(Collectors.toMap(oauth -> oauth.toLowerCase() + "登录", oauth -> "http://oauth.xkcoding.com/demo/oauth/login/" + oauth.toLowerCase()));
    }

    /**
     * 登录
     *
     * @param oauthType 第三方登录类型
     * @param response  response
     * @throws IOException
     */
    @ResponseBody
    @RequestMapping("/oauth/login/{oauthType}")
    public void renderAuth(@PathVariable String oauthType, HttpServletResponse response) throws IOException {
        AuthRequest authRequest = factory.get(getAuthSource(oauthType));
        response.sendRedirect(authRequest.authorize(oauthType + "::" + AuthStateUtils.createState()));
    }

    /**
     * 登录成功后的回调
     *
     * @param oauthType 第三方登录类型
     * @param callback  携带返回的信息
     * @return 登录成功后的信息
     */
    @RequestMapping(value = { "/oauth/{oauthType}/callback",
            Constants.SITE_PREFIX_PATH + "" + "/{oauthType}/callback" })
    public String login(@PathVariable String oauthType, AuthCallback callback, org.springframework.ui.Model modelMap, HttpServletRequest request,HttpServletResponse response, RedirectAttributes ra) throws IOException {
//        AuthRequest authRequest = factory.get(getAuthSource(oauthType));
//        AuthResponse response = authRequest.login(callback);
//        log.info("【response】= {}", JSONUtil.toJsonStr(response));
//        return response;

        String openid=request.getParameter("openid");
        //第三方登录扫码成功后，获取到openid，判断该openid是否被绑定，没有就跳到绑定页面
        if(StringUtils.isBlank(openid)){
            openid="97329472947189734913";
        }

        String nikename="我心永恒";
        User user = null;
        if("qq".equals(oauthType.toLowerCase())){
            user=userShiroService.findByQqOpenid(openid);
        }
        if("wechat".equals(oauthType.toLowerCase())){
            user=userShiroService.findByWeixinOpenid(openid);
        }
        if("weibo".equals(oauthType.toLowerCase())){
            user=userShiroService.findByWeiboUid(openid);
        }

        UserToken token=new UserToken(null,null,LoginType.THIRD_PARTY,openid,null);
        token.setProvider(oauthType);
        token.setUsername(nikename);
        token.setNickname(nikename);

        if(user==null){
            //还未绑定用户
            //Site site = Context.getCurrentSite();
            //Map<String, Object> data = modelMap.asMap();
            //ForeContext.setData(data, request);
            //modelMap.addAttribute(FALLBACK_URL_PARAM, "");
            ra.addAttribute(FALLBACK_URL_PARAM, "");
            HttpSession session = request.getSession();
            session.setAttribute("oauthToken",token);
            return "redirect:/oauth/register";
        }
        //已经存在就执行登录
        request.setAttribute("openid",openid);
        request.setAttribute("loginType", LoginType.THIRD_PARTY.name());

        Subject subject = SecurityUtils.getSubject();
        // 防止session fixation attack(会话固定攻击)，让旧session失效
        if (subject.getSession(false) != null) {
            subject.logout();
        }
        subject.login(token);


        return "redirect:/";

    }

    @GetMapping(value = { "/oauth/register",
            Constants.SITE_PREFIX_PATH + "" + "/oauth/register" })
    public String toRegister(String fallbackUrl, org.springframework.ui.Model modelMap, HttpServletRequest request,HttpServletResponse response, RedirectAttributes ra) throws IOException {
        Site site = Context.getCurrentSite();
        Map<String, Object> data = modelMap.asMap();
        ForeContext.setData(data, request);
        modelMap.addAttribute(FALLBACK_URL_PARAM, fallbackUrl);
        return site.getTemplate(OAUTH_REGISTER_TEMPLATE);
    }

    /**
     * 注册
     * @param fallbackUrl
     * @param username
     * @param oauthType
     * @param openid
     * @param gender
     * @param qq
     * @param msn
     * @param weixin
     * @param modelMap
     * @param request
     * @param response
     * @param ra
     * @return
     * @throws IOException
     */
    @PostMapping(value = { "/oauth/register",
            Constants.SITE_PREFIX_PATH + "" + "/oauth/register" })
    public String register(String fallbackUrl,String username,String oauthType,String openid, String gender, String qq, String msn, String weixin,org.springframework.ui.Model modelMap, HttpServletRequest request,HttpServletResponse response, RedirectAttributes ra) throws IOException {
        Response resp = new Response(request, response, modelMap);
        Site site = Context.getCurrentSite();
        GlobalRegister reg = site.getGlobal().getRegister();

        //注册并登录
        if(StringUtils.isBlank(username)||StringUtils.isBlank(openid)||StringUtils.isBlank(oauthType)){
            return "redirect:/";
        }
        User registerUser=null;
        String qqOpenid=null;
        String wechatOpenid=null;
        String weiboOpenid=null;
        if("qq".equals(oauthType.toLowerCase())){
            qqOpenid=openid;
            registerUser=userShiroService.findByQqOpenid(openid);
        }
        if("wechat".equals(oauthType.toLowerCase())){
            wechatOpenid=openid;
            registerUser=userShiroService.findByWeixinOpenid(openid);
        }
        if("weibo".equals(oauthType.toLowerCase())){
            weiboOpenid=openid;
            registerUser=userShiroService.findByWeiboUid(openid);
        }
        UserToken token=new UserToken(null,null,LoginType.THIRD_PARTY,openid,null);
        Subject subject = SecurityUtils.getSubject();
        if(registerUser!=null){
            //执行登录

            // 防止session fixation attack(会话固定攻击)，让旧session失效
            if (subject.getSession(false) != null) {
                subject.logout();
            }
            subject.login(token);

            return "redirect:/";
        }

        if (reg.getMode() == GlobalRegister.MODE_OFF) {
            return resp.warning("register.off");
        }

        registerUser = userService.findByUsername(username);
        if(registerUser==null){
            //注册
            int verifyMode = reg.getVerifyMode();
            String ip = Servlets.getRemoteAddr(request);
            int groupId = reg.getGroupId();
            int orgId = reg.getOrgId();
            int status = User.NORMAL;
            User user = userService.register(ip, groupId, orgId, status, username,
                    null,null, null, qqOpenid, weiboOpenid, wechatOpenid, gender, null, null,
                    null, qq, msn, weixin);
        }

        //执行登录
        // 防止session fixation attack(会话固定攻击)，让旧session失效
        if (subject.getSession(false) != null) {
            subject.logout();
        }
        subject.login(token);

        return "redirect:/";
    }


    /**
     * 绑定已有账号
     * @param fallbackUrl
     * @param modelMap
     * @param request
     * @param response
     * @param ra
     * @return
     * @throws IOException
     */
    @PostMapping(value = { "/oauth/bind",
            Constants.SITE_PREFIX_PATH + "" + "/oauth/bind" })
    public String bind(String fallbackUrl,@RequestParam(DEFAULT_USERNAME_PARAM) String username,String password,String oauthType,String openid, org.springframework.ui.Model modelMap, HttpServletRequest request,HttpServletResponse response, RedirectAttributes ra) throws IOException {
        try {
            UserToken token = new UserToken(username, password, LoginType.USER_PASSWORD, openid, null);
            Subject subject = SecurityUtils.getSubject();

            //执行登录
            // 防止session fixation attack(会话固定攻击)，让旧session失效
            if (subject.getSession(false) != null) {
                subject.logout();
            }
            subject.login(token);
            User user = userService.findByUsername(username);
            if (user != null) {
                //更新用户数据
                if ("qq".equals(oauthType.toLowerCase())) {
                    user.setQqOpenid(openid);
                    userService.update(user, user.getDetail());
                }
                if ("wechat".equals(oauthType.toLowerCase())) {
                    user.setWeixinOpenid(openid);
                    userService.update(user, user.getDetail());
                }
                if ("weibo".equals(oauthType.toLowerCase())) {
                    user.setWeiboUid(openid);
                    userService.update(user, user.getDetail());
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return "redirect:/";
    }



    private AuthSource getAuthSource(String type) {
        if (StrUtil.isNotBlank(type)) {
            return AuthSource.valueOf(type.toUpperCase());
        } else {
            throw new RuntimeException("不支持的类型");
        }
    }
}
